package br.cn.evo.controller;

import br.cn.evo.util.EnumTypes;
import br.cn.evo.util.EnumTypes.FitnessPart;
import br.cn.evo.util.Formulas;
import br.cn.evo.util.EnumTypes.Function;
import br.cn.evo.util.GlobalParameters;
	
public class Fitness {
	
	public static double distancex = Double.MAX_VALUE;
	public static double remainingFuel = GlobalParameters.fuelCapacity;
	
	private static double sphere(double[] position){
		double sum = 0.0;
		double pos;
		for (int i = 0; i < position.length; i++) {
			pos = position[i];
			sum += pos*pos;
		}
		return sum;
	}
	
	private static double sum(double position[]){
		double sum = 0.0;
		for (int i = 0; i < position.length; i++) {
			sum += position[i];
		}
		return sum;
	}
	
	private static double fuel(double pos[], boolean tremPouso, double tanque, 
			int passageiros, double peso, double wingArea){
		
		double finalFitness = 0.0;
		double finalDistance = 0.0;
		double finalTime = 0.0;
		
		//remainingFuel = GlobalParameters.fuelCapacity;
		double totalFuel = pos[15];
		remainingFuel = pos[15];
		
		double pistaOrigem = GlobalParameters.trackAscendingDim;
		double pistaDestino = GlobalParameters.trackDescendingDim;
		
		
		//Chamar todas as formulas da classe Formulas
		//Organizar, calcular e devolver o fitness geral
		
		
		//---------------------------------------------------------------------------------
		//Calculo para 1
		pos[1] = 0; //AoA = 0�
		double vel1 = Formulas.velocityByThrust(pos[0]);
		
		double f1 = Formulas.lift(FitnessPart.I, wingArea, vel1, pos[1]);
		if(f1 >= peso){
			finalFitness += f1;
		}else{
			return GlobalParameters.fuelCapacity;
		}
		//aceleração de arranque
		if(pos[2] <= 0.0) pos[2] = -1*pos[2];
				
		//verificar se deu tempo pra subir
		double time1 = Formulas.TIME(vel1, 0, pos[2]);
		double distance1 = Formulas.MUV(0, 0, pos[2], time1);
		
		//double l1 = (distance1/1000) / GlobalParameters.kml;
		double l1 = (distance1/1000) / Formulas.fuelWaste(pos[0]);
		
		if(distance1 > pistaOrigem) return Double.MAX_VALUE;
		if(l1 >= remainingFuel){
			return GlobalParameters.fuelCapacity;
		}else{
			remainingFuel -= l1;
			peso -= l1;
		}
		
		double v1 = vel1/2; //velocidade media
		
		
		
		//---------------------------------------------------------------------------------
		//Calculo para 2
		double vel2 = Formulas.velocityByThrust(pos[3]);
		double f2 = Formulas.lift(FitnessPart.II, wingArea, vel2, pos[4]);
		if(f2 >= peso){
			finalFitness += f2;
		}else{
			return GlobalParameters.fuelCapacity;
		}
		
		//verificar tempo de subida
		double distance2 = Formulas.VERTICAL(vel2, pos[4], GlobalParameters.altitude);
		double a2 = Formulas.TORRI_ACCEL(distance2, vel2, vel1);
		
		pos[5] = a2;
		
		double time2 = Formulas.TIME(vel2, vel1, a2);
		double distanceH = Formulas.MUV(0, vel1, pos[5], time2);
		
		double l2 = (distance2/1000) / Formulas.fuelWaste(pos[3]);
		if(l2 >= remainingFuel){
			return GlobalParameters.fuelCapacity;
		}else{
			remainingFuel -= l2;	
			peso -= l2;
		}
		
		double v2 = (vel2+vel1)/2; //velocidade media
		
		
		//---------------------------------------------------------------------------------
		//Calculo para 3
		pos[7] = 0.0; //voo em cruzeiro | AoA = 0�
		double vel3 = Formulas.velocityByThrust(pos[6]);
		double f3 = Formulas.lift(FitnessPart.III, wingArea, vel3, pos[7]);
		if(f3 >= peso){
			finalFitness += f3;
		}else{
			return GlobalParameters.fuelCapacity;
		}
		double distance3= GlobalParameters.distanceFlight;
		double a3 = Formulas.TORRI_ACCEL(distance3, vel3, vel2);
		
		pos[8] = a3;
		
		double time3 = Formulas.TIME(vel3, vel2, a3);
		
		double l3 = (distance3/1000) / Formulas.fuelWaste(pos[6]);
		if(l3 >= remainingFuel){
			return GlobalParameters.fuelCapacity;
		}else{
			remainingFuel -= l3;	
			peso -= l3;
		}
		
		double v3 = (vel3+vel2)/2; //velocidade media
		
		//---------------------------------------------------------------------------------
		//Calculo para 4
		double vel4 = Formulas.velocityByThrust(pos[9]);
		double f4 = Formulas.lift(FitnessPart.IV, wingArea, vel4, pos[10]);
		if(f4 >= peso){
			finalFitness += f4;
		}else{
			return GlobalParameters.fuelCapacity;
		}
		double distance4 = Formulas.VERTICAL(vel4, pos[10], GlobalParameters.altitude);
		double a4 = Formulas.TORRI_ACCEL(distance4, vel4, vel3);
		
		pos[11] = a4;
		
		double time4 = Formulas.TIME(vel4, vel3, a4);
		double distanceH2 = Formulas.MUV(0, vel3, a4, time4);
		
		double l4 = (distance4/1000) / Formulas.fuelWaste(pos[9]);
		if(l4 >= remainingFuel){
			return GlobalParameters.fuelCapacity;
		}else{
			remainingFuel -= l4;
			peso -= l4;
		}
		
		double v4 = (vel4+vel3)/2; //velocidade media
		
		//---------------------------------------------------------------------------------
		//Calculo para 5
		pos[13] = 0; // AoA = 0
		pos[12] = 0; //Velocidade final = 0
		//pos[12] = 0.1; // freio
		
		double f5 = Formulas.lift(FitnessPart.V, wingArea, pos[12], pos[13]);
		if(f5 == 0){
			//finalFitness += f5;
		}else{
			return GlobalParameters.fuelCapacity;
		}
		//acelera��o de Descida
		if(pos[14] >= 0.0) pos[14] = -1*pos[14];

		//verificar se deu tempo pra subir
		double time5 = Formulas.TIME(pos[12], vel4, pos[14]);
		double distance5 = -Formulas.MUV(0, vel4, pos[14], time5);

		if(distance5 > pistaDestino) return Double.MAX_VALUE;
		
		double l5 = (distance5/1000) / Formulas.fuelWaste(pos[12]);
		if(l5 > remainingFuel){
			return GlobalParameters.fuelCapacity;
		}else{
			remainingFuel -= l5;
			peso -= l5;
		}
		
		double v5 = pos[9]/2;
		
		//----------------------------------------------------------------------------------
		//Outros calculos
		finalDistance = distance1+distance2+distance3+distance4+distance5;
		
		//return finalFitness/4;
		//return finalDistance;
		
		return totalFuel;
	}
	
	public static double getFitness(EnumTypes.Function function, double[] position, boolean tremPouso, double tanque, 
			int passageiros, double peso, double wingArea){
		switch (function) {
		case Sphere:
			return sphere(position);
		case Sum:
			return sum(position);
		case Fuel:
			return fuel(position, tremPouso, tanque, 
					passageiros, peso, wingArea);
		default:
			return 0.0;
		}
	}
	
	public static double getMax(Function function, Integer x){
		switch (function) {
		case Sphere:
			return 5.12;
		case Sum:
			return 100000;
		case Fuel:
			//par = velocity | impar = AoA
			if(x == null)
				return 0.0;
			//Thrust
			if( (x == 0) || (x == 3) || (x == 6) || (x == 9) || (x == 12) )
				return GlobalParameters.maxThrust;
			//AoA
			if( (x == 1) || (x == 4) || (x == 7) || (x == 10) || (x == 13) )
				return 30.0;
			//Accel
			if( (x == 2) || (x == 5) || (x == 8) || (x == 11) || (x == 14) )
				return 3;
		default:
			return GlobalParameters.fuelCapacity;
		}
	}
	
	public static double getMin(Function function, Integer x){
		switch (function) {
		case Sphere:
			return -5.12;
		case Sum:
			return -100000;
		case Fuel:
			//par = velocity | impar = AoA
			if(x == null)
				return 0.0;
			//Thrust
			if( (x == 0) || (x == 3) || (x == 6) || (x == 9) || (x == 12) )
				return 0.1;
			//AoA
			if( (x == 1) || (x == 4) || (x == 7) || (x == 10) || (x == 13) )
				return 0.0;
			//Accel
			if( (x == 2) || (x == 5) || (x == 8) || (x == 11) || (x == 14) )
				return -50; //frenagem + motor invertido
		default:
			return 0.0;
		}
	}
	
}
